package com.work.servlet;

import com.work.annotation.*;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @Author:ChenZhangKun
 * @Date: 2020/12/3 20:13
 */
public class DispatcherServlet extends HttpServlet {
    /**
     * 保存文件地址
     */
    List<String> classPaths=new ArrayList<>(10);
    /**
     * Ioc容器
      */
    Map<String,Object> beans=new HashMap<>(16);
    /**
     * 存储方法的容器
     */
    Map<String,Object> handlerMap=new HashMap<>(10);

    @Override
    public void init() throws ServletException {
        System.out.println("实例化自己的前端控制器");
        // 扫描所有的类
        doScanPackage("com.work");
        // 实例化
        doInstance();
        // 依赖注入
        doAutowired();
        // 地址和方法映射
        urlMapping();
        System.out.println("xxx");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取请求地址
        String url = request.getRequestURI();
        // 第一个/的索引
        int index = url.indexOf("/", 1);
        // 截取到/hello
        String substring = url.substring(0, index);
        // 拿到该方法
        Method method=(Method) handlerMap.get(url);
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(CzkController.class)){
                // 拿到值
                CzkRequestMapping annotation = clazz.getAnnotation(CzkRequestMapping.class);
                String value = annotation.value();
                if (Objects.equals(substring,value)){
                    // 执行方法
                    try {
                        Object[] args=getArgs(request,response,method);
                        method.invoke(entry.getValue(),args);
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                }
            }
        }
    }

    private Object[] getArgs(HttpServletRequest request, HttpServletResponse response, Method method) {
        // 拿到当前类待执行的方法参数
        Class<?>[] clazzParams = method.getParameterTypes();
        Object[] args=new Object[clazzParams.length];
        int args_i=0;
        int index=0;
        // 遍历
        for (Class<?> clazzParam : clazzParams) {
            // 判断class对象锁表示的类与指定的Class参数表示类或接口是否相同，或者是否其超类或超接口
            if (ServletRequest.class.isAssignableFrom(clazzParam)){
                args[args_i++]=request;
            }
            if (ServletResponse.class.isAssignableFrom(clazzParam)){
                args[args_i++]=response;
            }
            // 判断有没@requestparam注解
            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if (paramAns.length>0){
                for (Annotation paramAn : paramAns) {
                    if (CzkRequestParam.class.isAssignableFrom(paramAn.getClass())){
                        // 强转
                        CzkRequestParam czkRequestParam=(CzkRequestParam)paramAn;
                        args[args_i++]=request.getParameter(czkRequestParam.value());
                    }
                }
            }
            index++;
        }
        return args;
    }

    private void urlMapping() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            // 是否是控制类
            if (clazz.isAnnotationPresent(CzkController.class)){
                // 拿到RequestMapping的注解
                CzkRequestMapping annotation = clazz.getAnnotation(CzkRequestMapping.class);
                // 拿到访问地址
                String classpath = annotation.value();
                // 拿到所有方法
                Method[] declaredMethods = clazz.getDeclaredMethods();
                // 遍历
                for (Method declaredMethod : declaredMethods) {
                    if (declaredMethod.isAnnotationPresent(CzkRequestMapping.class)){
                        // 拿到其值
                        CzkRequestMapping methodAnnotation = declaredMethod.getAnnotation(CzkRequestMapping.class);
                        String methodPath = methodAnnotation.value();
                        // 拼接
                        handlerMap.put(classpath+methodPath,declaredMethod);
                    }else {
                        continue;
                    }
                }
            }

        }
    }

    private void doAutowired() {
        Set<Map.Entry<String, Object>> entries = beans.entrySet();
        // 遍历map
        for (Map.Entry<String, Object> entry : entries) {
            // 拿到类型
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(CzkController.class)){
                // 获取被private修饰的属性
                Field[] declaredFields = clazz.getDeclaredFields();
                // 遍历
                for (Field field : declaredFields) {
                    // 是否被autowired修饰
                    if (!field.isAnnotationPresent(CzkAutoWired.class)) continue;
                    // 进行注入
                    CzkAutoWired czkAutoWired = field.getAnnotation(CzkAutoWired.class);
                    String beanName;
                    if (Objects.equals("",czkAutoWired.value())){
                        // 默认名称注入
                        beanName=lowerFirstChar(field.getType().getSimpleName());
                    }else {
                        // 名称注入
                        beanName=czkAutoWired.value();
                    }
                    // 设置访问修饰符
                    field.setAccessible(true);
                    if (beans.get(beanName)!=null){
                        try {
                            field.set(entry.getValue(),beans.get(beanName));
                        }catch (IllegalAccessException ex){
                            ex.printStackTrace();
                        }
                    }
                }
            }


        }

    }

    private void doInstance() {
        // 遍历所有的类
        for (String classPath : classPaths) {
            try {
                // 去掉class
                Class<?> clazz = Class.forName(classPath.replace(".class", ""));
                // 判断是都被controller修饰
                if (clazz.isAnnotationPresent(CzkController.class)){
                    // 控制类
                    Object o = clazz.newInstance();
                    beans.put(lowerFirstChar(clazz.getSimpleName()),o);
                }else if (clazz.isAnnotationPresent(CzkService.class)){
                    // service
                    CzkService czkService = clazz.getAnnotation(CzkService.class);
                    // 拿到值
                    String key = czkService.value();
                    if (!Objects.equals(key,"")){
                        // 放入集合
                        beans.put(key,clazz.newInstance());
                        continue;
                    }
                    // 如果没有设置name则接口的首字母小写
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        // 接口首字母小写
                        Object o = clazz.newInstance();
                        beans.put(lowerFirstChar(anInterface.getSimpleName()),o);
                        break;
                    }
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }

    }

    private String lowerFirstChar(String simpleName) {
        String substring = simpleName.substring(0, 1);
        String s = substring.toLowerCase();
        String newSimpleName=s+simpleName.substring(1);
        return newSimpleName;
    }

    private void doScanPackage(String com) {
        // 获取所有的com包下的类文件地址
        URL url = this.getClass().getClassLoader().getResource("/" + com.replaceAll("\\.", "/"));
        // 拿到文件地址
        String fileStr = url.getFile();
        // 转为文件类型
        File file=new File(fileStr);
        // 获取里面所有文件
        String[] files = file.list();
        // 遍历
        for (String file1 : files) {
            File filePath=new File(fileStr+file1);
            // 判断是否是目录
            if (filePath.isDirectory()){
                doScanPackage(com+"."+file1);
            }else {
                classPaths.add(com+"."+file1);
            }
        }
    }
}
